package com.yydream.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yydream.common.constant.HttpStatus;
import com.yydream.common.constant.RedisKeyEnums;
import com.yydream.common.utils.JwtUtils;
import com.yydream.common.utils.R;
import com.yydream.common.utils.RedisCache;
import com.yydream.domain.LoginUser;
import com.yydream.dto.CommonUserDTO;
import com.yydream.entity.SysUserEntity;
import com.yydream.mapper.SysUserMapper;
import com.yydream.service.LoginService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class LoginServiceImpl implements LoginService, UserDetailsService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;

    @Value("${file.uploadPath}")
    private String uploadPath;

    @Value("${server.servlet.context-path}")
    private String contextPath;

    @Value("${file.filePattern}")
    private String filePattern;

    @Value("${file.imgTypes}")
    private String imgTypes;

    @Value("${file.fileTypes}")
    private String fileTypes;

    @Override
    public R login(CommonUserDTO user, HttpServletRequest request) {
        if (validCaptcha(user, request)) return R.error(HttpStatus.PARAM_ERROR.getCode(), "验证码校验失败");
        //AuthenticationManager authenticate 进行用户认证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        //如果认证没通过，给出对应的提示
        if (Objects.isNull(authenticate)){
            throw new RuntimeException("登录失败");
        }
        //如果认证通过了，使用userid生成一个jwt jwt存入ajax
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        Long userId = loginUser.getUserEntity().getId();
        Map<String,Object> map = new HashMap<>();
        map.put(JwtUtils.DETAILS_USER_ID,userId);
        String token = JwtUtils.createToken(map);
        //把完整的用户信息存入redis userid作为key
        redisCache.setCacheObject(RedisKeyEnums.LOGIN.getKey()+userId,loginUser);
        return R.ok().put( token);
    }

    @Override
    public R logout() {
        //获取SecirityContextHolder中的用户id
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long id = loginUser.getUserEntity().getId();
        //删除redis中的值
        redisCache.deleteObject(RedisKeyEnums.LOGIN.getKey() + id);
        return R.ok();
    }

    @Override
    public R register(CommonUserDTO userDto, HttpServletRequest request) {
        if (validCaptcha(userDto, request)) return R.error(HttpStatus.PARAM_ERROR.getCode(), "验证码校验失败");
        SysUserEntity userEntity = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUserEntity>().eq(SysUserEntity::getUsername, userDto.getUsername()));
        if (Objects.nonNull(userEntity)) {
            return R.error(HttpStatus.USER_EXIST.getCode(), HttpStatus.USER_EXIST.getMsg());
        }
        String hashpw = BCrypt.hashpw(userDto.getPassword());
        userDto.setPassword(hashpw);
        SysUserEntity sysUserEntity = new SysUserEntity();
        BeanUtils.copyProperties(userDto, sysUserEntity);
        sysUserEntity.setNickname(userDto.getUsername());
        sysUserEntity.setState(1);
        sysUserMapper.insert(sysUserEntity);
        return R.ok();
    }

    private boolean validCaptcha(CommonUserDTO userDto, HttpServletRequest request) {
        HttpSession session = request.getSession();
        String id = session.getId();
        String captchaCode = redisCache.getCacheObject(RedisKeyEnums.CAPTCHA.getKey() + id);
        if (StringUtils.isEmpty(captchaCode) || !userDto.getCaptcha().equals(captchaCode)) {
            return true;
        }
        redisCache.deleteObject(RedisKeyEnums.CAPTCHA.getKey() + id);
        return false;
    }

    @Override
    public R captcha(HttpServletRequest request) {
        HttpSession session = request.getSession();
        String id = session.getId();
        RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(130, 48);
        lineCaptcha.setGenerator(randomGenerator);
        String imageBase64 = lineCaptcha.getImageBase64();
        String base64 = "data:image/jpg;base64," + imageBase64;
        redisCache.setCacheObject(RedisKeyEnums.CAPTCHA.getKey()+id,lineCaptcha.getCode());
        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("img", base64);
//        stringObjectHashMap.put("key", id);
        return R.ok().put( stringObjectHashMap);
    }

    @Override
    public R uploadFile(MultipartFile file) {
        return upload(fileTypes, file);
    }

    @Override
    public R uploadImg(MultipartFile file) {
        return upload(imgTypes, file);
    }

    public R upload (String types, MultipartFile file) {
        String[] imgTypeList = types.split(",");
        Boolean isFlag = false;
        String oldName = file.getOriginalFilename();
        for (String type : imgTypeList) {
            if (StringUtils.endsWithIgnoreCase(oldName, type)) {
                isFlag = true;
                break;
            }
        }
        if (!isFlag) {
            return R.error(HttpStatus.PARAM_ERROR.getCode(), "格式必须为：" + imgTypes);
        }
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String format = dateFormat.format(new Date());
        String middlePath = "/" + format  + "/";
        File fileUpload = new File(uploadPath + middlePath);
        if (!fileUpload.isDirectory()) {
            fileUpload.mkdirs();
        }
        String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."), oldName.length());
        try {
            File newFile = new File(fileUpload.getAbsolutePath() + File.separator + newName);
            file.transferTo(newFile);
            String filePath = contextPath + "/" +  filePattern + middlePath + newName;
            Map<String, String> stringStringHashMap = new HashMap<>();
            stringStringHashMap.put("path", filePath);
            stringStringHashMap.put("name", oldName);
            return R.ok().put( stringStringHashMap);
        } catch (IOException e) {
            return R.error(e.getMessage());
        }
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询用户信息
        LambdaQueryWrapper<SysUserEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUserEntity::getUsername,username);
        SysUserEntity user = sysUserMapper.selectOne(queryWrapper);
        //如果没有查询到用户就抛出异常
        if (Objects.isNull(user)) {
            throw new RuntimeException("用户名或者密码错误");
        }
        if (user.getState() == 2) {
            throw new RuntimeException("账号已经被禁用");
        }
        // TODO 查询对应的权限信息
        List<String> list = new ArrayList<>(Arrays.asList("test","admin"));
        //把数据封装成UserDetails返回
        return new LoginUser(user,list);
    }
}
